import { AssetManager, assetManager, Prefab, resources, SpriteFrame } from 'cc';
import { DEBUG, PREVIEW } from 'cc/env';

type CompleteCallback<T = any> = (err: Error | null, data: T) => void;
class LoadBundleData {
    // 资源路径
    path: string = "";
    // 是否为文件夹
    isDir: boolean = false;
    // type
    type: any = null;
    // 加载包内指定资源后的回调
    loadFileCall: CompleteCallback = null;

    constructor(path: string, type: any, call: CompleteCallback, isDir: boolean) {
        this.path = path;
        this.isDir = isDir;
        this.type = type;
        this.loadFileCall = call;
    }
}


export interface IFileExtData {
    // 文件名
    resName: string;
    // 文件指定的语言
    lang?: string;
    // 包内路径
    path?: string;
}

/**
 * Cocos Creator Bundle方法拓展
 */
export default class BundleExt {

    // 获取指定的Bundle包
    private static getBundle(bundleName?: string): AssetManager.Bundle {
        // cocos默认bundle
        if (!bundleName || bundleName == "resources")
            return resources;

        return assetManager.getBundle(bundleName);
    }
    // 加载自定义分包
    private static loadBundle(bundleName: string, data: LoadBundleData) {
        const bundleMap = BundleExt.bundleLoadMap;
        if (!bundleMap.has(bundleName)) {
            // 首次加载指定包
            bundleMap.set(bundleName, [data]);
            assetManager.loadBundle(bundleName, (error: Error | null, bundle: AssetManager.Bundle) => {
                const list: LoadBundleData[] = BundleExt.bundleLoadMap.get(bundleName) as Array<LoadBundleData>;
                if (!error && !!bundle) {
                    // 加载成功，并获得包资源
                    if (!!list) {
                        setTimeout(() => {
                            list.forEach(info => {
                                if (info.isDir)
                                    bundle.loadDir(info.path, info.type, info.loadFileCall);
                                else
                                    bundle.load(info.path, info.type, info.loadFileCall);
                            });
                            list.splice(0);
                        }, 0);
                    }
                }
                else {
                    // 加载失败
                    if (!!list)
                        list.splice(0);
                    BundleExt.bundleLoadMap.set(bundleName, -1);
                }
            });
        }
        else {
            const info: LoadBundleData[] | -1 = bundleMap.get(bundleName);
            if (info === -1) {
                // 曾经加载过，但是失败了
                DEBUG && console.debug("[BundleExt] bundle undefined, bundleName = ", bundleName);
                data.loadFileCall(new Error("bundle undefined!"), null);
            }
            else {
                // 正在加载期间
                (info as Array<LoadBundleData>).push(data);
            }
        }
    }
    // 分包加载回调集合
    static bundleLoadMap: Map<string, LoadBundleData[] | -1> = new Map();

    /**
     * 格式化资源路径
     * @param resName 文件名
     * @returns 
     */
    static fixPath(info: IFileExtData): string {
        if (!info)
            return "";
        let str = info.resName;
        if (!!info.path) {
            let path = info.path;
            const endSlash = path[path.length - 1] == '/';
            if (!endSlash)
                path += "/";
            str = path + str;
        }
        if (!!info.lang)
            str += "_" + info.lang;
        return str;
    }

    static loadDir(dir: string, type: any, onComplete: CompleteCallback, bundleName?: string) {
        // bundle
        const bdl = BundleExt.getBundle(bundleName);
        if (bdl)
            bdl.loadDir(dir, type, onComplete);
        else {
            BundleExt.loadBundle(bundleName, new LoadBundleData(dir, type, onComplete, true));
        }
    }

    /**
     * 加载分包内的图片纹理
     * @param path 文件包内完整路径
     * @param onComplete 加载完成回调
     * @param bundleName 分包名称
     */
    static loadSprite(path: IFileExtData | string, onComplete: CompleteCallback, bundleName?: string) {
        // path
        let fixPath: string = typeof path !== "string" ? BundleExt.fixPath(path) : path;
        if (!fixPath) {
            DEBUG && console.debug("[BundleExt] img path undefined!");
            onComplete(new Error("img path undefined!"), null);
            return;
        }
        fixPath += "/spriteFrame";

        // bundle
        const bdl = BundleExt.getBundle(bundleName);
        if (bdl)
            bdl.load(fixPath, SpriteFrame, onComplete);
        else {
            BundleExt.loadBundle(bundleName, new LoadBundleData(fixPath, SpriteFrame, onComplete, false));
        }
    }

    /**
     * 加载分包内的预制体
     * @param path 文件包内完整路径
     * @param onComplete 加载完成回调
     * @param bundleName 分包名称
     */
    static loadPrefab(path:string, onComplete: CompleteCallback, bundleName?: string){
        const bdl = BundleExt.getBundle(bundleName);
        if (bdl)
            bdl.load(path, Prefab, onComplete);
        else {
            BundleExt.loadBundle(bundleName, new LoadBundleData(path, Prefab, onComplete, false));
        }
    }

}
if (PREVIEW) globalThis.BundleExt = BundleExt;
